package org.shagin.jfin.core;

import java.util.Vector;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The Class Debug.
 */
public class Debug {
    private static final Logger log = LoggerFactory.getLogger(Debug.class);

    private static int L5 = 5;
    private static int METHOD = 0;
    private static int CALLER = METHOD + 1;

    private static final String LEAVE = "leave";
    private static final String ENTER = "enter";

    /**
     * Enter.
     */
    public static void traceEnter() {
        doLog(log, ENTER, L5, METHOD, StringUtils.EMPTY);
    }

    /**
     * Enter.
     * 
     * @param logger4j the logger4j
     */
    public static void traceEnter(Logger log) {
        doLog(log, ENTER, L5, METHOD, StringUtils.EMPTY);
    }

    /**
     * Trace enter.
     * 
     * @param logger4j the logger4j
     * @param params the params
     */
    public static void traceEnter(Logger logger, String params) {
        doLog(logger, ENTER, L5, METHOD, params);
    }

    public static void traceEnter(Logger logger, Object... params) {
        doLog(logger, ENTER, L5, METHOD, paramsToString(params));
    }

    /**
     * Params to string.
     * 
     * @param params the params
     * @return the string
     */
    private static String paramsToString(Object... params) {
        return StringUtils.join(params, ", ");
    }

    /**
     * Leave.
     */
    public static void traceLeave() {
        doLog(log, LEAVE, L5, METHOD, StringUtils.EMPTY);
    }

    /**
     * Trace leave.
     * 
     * @param logger4j the logger4j
     */
    public static void traceLeave(Logger log) {
        doLog(log, LEAVE, L5, METHOD, StringUtils.EMPTY);
    }
    
    /**
     * Error.
     *
     * @param log the log
     * @param ex the ex
     */
    public static void error(Logger log, Exception ex) {
        doLogError(log, L5, METHOD, ex);
    }

    /**
     * Do log.
     * 
     * @param logger the logger
     * @param typ the typ
     * @param offset the offset
     * @param level the level
     * @param params the params
     */
    private static void doLog(Logger logger, String typ, int offset, int level,
            String params) {
        if (!logger.isTraceEnabled())
            return;
        String methodName = getCallingMethodDesciption(offset, level, params);
        if (!StringUtils.isEmpty(methodName))
            logger.trace(typ + " " + methodName);
    }

    /**
     * Do log error.
     *
     * @param logger the logger
     * @param offset the offset
     * @param level the level
     * @param ex the ex
     */
    private static void doLogError(Logger logger, int offset, int level,
            Exception ex) {
        if (!logger.isErrorEnabled())
            return;
        String methodName = getCallingMethodDesciption(offset, level, null);
        if (!StringUtils.isEmpty(methodName))
            logger.error(methodName+" : ", ex);
    }

    /**
     * Gets the calling method desciption.
     * 
     * @param offset the offset
     * @param level the level
     * @param params the params
     * @return the calling method desciption
     */
    private static String getCallingMethodDesciption(int offset, int level,
            String params) {
        String result = null;
        StackTraceElement[] ste = getStackTraceElement(offset);
        if (ste != null)
            result = ste[METHOD].getMethodName()
                    + (StringUtils.isEmpty(params) ? "" : "(" + params + ")")
                    + " from " + ste[CALLER].getMethodName() + " ["
                    + ste[CALLER].getClassName() + "]" + " - (" + ste.length
                    + ")";
        return result;
    }

    /**
     * Gets the stack trace element.
     * http://swbyjeff.com/index.php/Dynamically_Retreive_Method_Name
     * 
     * @param offset the offset
     * @return the stack trace element
     */
    private static StackTraceElement[] getStackTraceElement(int offset) {

        StackTraceElement[] result = null;
        try {
            StackTraceElement[] a = Thread.currentThread().getStackTrace();
            result = new StackTraceElement[a.length - offset];
            for (int i = offset; i < a.length; i++) {
                result[i - offset] = a[i];
            }
        } catch (Exception e) {
            log.warn("Can't get stack trace", e);
        }

        return result;
    }

    /**
     * The main method.
     * 
     * @param args the arguments
     */
    public static void main(String[] args) {
        Test test = new Test();
        test.test1();
        test.test2();
    }

    /**
     * The Class Test.
     */
    private static class Test {
        private static Logger log = LoggerFactory.getLogger(Test.class);

        /**
         * Test1.
         */
        public void test1() {
            Debug.traceEnter();
            Debug.traceLeave();
        }

        /**
         * Test2.
         */
        public void test2() {
            Debug.traceEnter(log);
            test1();
            Debug.traceLeave(log);
        }
    }

    /**
     * To string.
     * 
     * @param obj the obj
     * @return the string
     */
    public static String toString(Object obj) {
        String result = "";
        if (null == obj) {
            result = "NULL";
        } else {
            if (obj instanceof Vector<?>) {
                @SuppressWarnings("unchecked")
                Vector<Object> v = (Vector<Object>) obj;
                result = v.size() + " : [";
                int i = 0;
                for (Object o : v)
                    result += (i++) + "<" + toString(o) + "> ";
                result += "]";
            } else
                result = obj.getClass().getName() + "[" + obj.toString() + "]";
        }
        return result;

    }

}
